import os
from typing import Optional
from pathlib import Path
import re

# 确保win10终端颜色正常显示
# if os.name == "nt":
#    os.system("")


DIR = Path(__file__).parent
TTF_DIR = DIR / "font"
BDF_DIR = DIR / "bdf"
TEXT_DIR = DIR / "text"
CODE_DIR = DIR / "code"


def input_or(prompt: str, default):
    r = input(prompt).strip()
    if len(r) == 0:
        return default
    if isinstance(default, int):
        return int(r)
    if isinstance(default, float):
        return float(r)
    return r


def select_file(dir: Path, ext: str, allow_skip: Optional[str]) -> Optional[Path]:
    if not dir.exists():
        return None
    file_list = list(dir.glob(f"*.{ext}"))

    if allow_skip is not None:
        print(f"(0) : {allow_skip}")

    if len(file_list) == 0:
        if allow_skip is not None:
            return dir
        else:
            return None

    for i in range(len(file_list)):
        print(f"({i + 1}) : {file_list[i].name}")

    r = -1
    while not (r >= 0 and r <= len(file_list)):
        if allow_skip:
            r = input_or("选择 = ", 0)
        else:
            r = input_or("选择 = ", -1)
        assert isinstance(r, int)

    if r == 0:
        return dir
    else:
        return file_list[r - 1]


def file_name_regulate(file_name: str, replace_with: str = '_') -> str:
    '''
    将文件名中的不合法字符替换为下划线，或用replace_with 指定的字符
    '''
    r = r"[\/\\\:\*\?\"\<\>\|\t]"
    return re.sub(r, replace_with, file_name).strip()


# 字符转unicode函数
def to_unicode(string):
    ret = ""
    for v in string:
        ret = ret + hex(ord(v)).upper().replace('0X', '$')
    return ret


def routine():
    # 启动信息
    print("U8g2字库生成器 V1.0")
    print("Email: createskyblue@outlook.com")
    print()

    # 输入模式，手动输入或读取文本文件
    print("【 输入模式 】(0)")

    rf = select_file(TEXT_DIR, "txt", "手动输入")
    text_file_name_no_ext = None
    assert rf is not None
    if rf == TEXT_DIR:
        inputText = input('手动输入 = ').strip()
    else:
        inputText = rf.read_text(encoding="utf-8").strip()
        text_file_name_no_ext = rf.stem
        print(inputText)

    print()

    if len(inputText) == 0:
        print("无输入")
        return

    # 获取生成目标字体名称
    target_input = ""
    while len(target_input) == 0:
        if text_file_name_no_ext is not None:
            prompt = f"字库命名({text_file_name_no_ext}) = u8g2_font_ "
            target_input = input(prompt).strip()
            if len(target_input) == 0:
                target_input = text_file_name_no_ext
        else:
            prompt = f"字库命名 = u8g2_font_ "
            target_input = input(prompt)
        target_input = file_name_regulate(target_input)

    targetFontName = f"u8g2_font_{target_input}"
    print(targetFontName)

    print()
    print("【 字体类型 】(0)")
    print("(0) : BDF")
    print("(1) : TTF")

    font_mode = input_or("选择 = ", 0)
    assert font_mode == 0 or font_mode == 1

    print()

    bdf_file = None
    ttf_file = None

    if font_mode == 0:
        # BDF 模式，不需要读取并转换TTF
        rf = select_file(BDF_DIR, "bdf", None)
        assert rf is not None
        bdf_file = rf
        bdfPath = bdf_file.absolute().as_posix()
    else:
        rf = select_file(TTF_DIR, "ttf", None)
        assert rf is not None
        ttf_file = rf

        # 获取字体文件
        ttfFontPath = ttf_file.as_posix()
        tftFontNameReal = ttf_file.stem

        # 获取字体DPI解析度
        fontSizeDPI = eval(input("输入字体DPI = "))
        # 获取字体px大小
        fontSizePx = eval(input("输入字体大小(px) = "))
        # 换算成单位pt
        fontSizePt = fontSizePx / (fontSizeDPI / 72)

        # 生成BDF 文件
        bdf_file = BDF_DIR / f"{tftFontNameReal}_{fontSizePx}.bdf"
        bdfPath = bdf_file.absolute().as_posix()
        print("生成BDF")
        otf2bdfCMD = "otf2bdf.exe -v -r {3} -p {0} -o {1} {2}".format(fontSizePt, bdfPath, ttfFontPath, fontSizeDPI)
        print("> ", otf2bdfCMD)
        os.system(otf2bdfCMD)

    # 获取字体间距大小%
    fontSPSize = input_or("输入字体间距大小(%) = ", 0)

    # 询问：输入是否过滤ascii
    del_ASCII_flag = False
    del_ASCII_flag_input = input("是否过滤ASCII? (Y/n)").strip()
    if len(del_ASCII_flag_input) == 0 or del_ASCII_flag_input == "y" or del_ASCII_flag_input == "Y":
        del_ASCII_flag = True

    # 询问：map字符映射文件是否包含ASCII
    add_ASCII_flag = False
    add_ASCII_flag_input = input("MAP字符映射文件是否包含ASCII? (Y/n)").strip()
    if len(del_ASCII_flag_input) == 0 or add_ASCII_flag_input == "y" or add_ASCII_flag_input == "Y":
        add_ASCII_flag = True

    print()

    # 创建原子字典，对目标字去重
    atomText = set(inputText)
    print("去重字库：", atomText)

    print()

    # 原子字典转unicode格式
    unicodeText = []
    for c in atomText:
        # 选择过滤ASCII结果
        if del_ASCII_flag and ord(c) < 255:
            continue
        # unicode转换
        unicodeText.append(to_unicode(c))
    # 对转换结果进行排序
    unicodeText.sort()
    print("转unicode原子字库：", unicodeText)

    if c := len(atomText) != len(unicodeText):
        print(f"去除字符数量 = {c}")

    print()

    # 生成map字符映射表
    mapFileDatas = ""
    # 选择性加入ASCII到map字符映射表
    if add_ASCII_flag:
        mapFileDatas += "32-128"
    # 合并unicode字典到映射表
    for u in unicodeText:
        if len(mapFileDatas) > 0:
            mapFileDatas += ", "  # 分隔符：这里必须要有空格
        mapFileDatas += u
    print("字符map映射表：", mapFileDatas)
    print()

    # 覆盖创建map文件
    mapPath = "map/{}.map".format(targetFontName)
    mapFile_f = open(mapPath, "w")
    mapFile_f.write(mapFileDatas)
    mapFile_f.close()

    c_code_file = CODE_DIR / f"{targetFontName}.c"
    c_codePath = c_code_file.absolute().as_posix()

    # 生成u8g2目标C语言字库文件
    print("生成C 代码文件")

    bdfconvCMD = "bdfconv.exe -v -b 0 -f 1 {0} -M {1} -n {2} -o {3} -p {4} -d {0}".format(
        bdfPath, mapPath, targetFontName, c_codePath, fontSPSize
    )
    print(">", bdfconvCMD)
    os.system(bdfconvCMD)

    print("")
    print("生成CPP 代码文件")

    c_src = c_code_file.read_text()
    cpp_code_file = CODE_DIR / f"{targetFontName}.cpp"
    cpp_src = f'#include <u8g2lib.h>\n\nextern "C" {{\n\n// {bdf_file.name}\n\n/*\n{inputText}\n*/\n\n{c_src}\n}}'
    cpp_code_file.write_text(cpp_src)

    # 完成信息
    print("[操作完成]")


if __name__ == "__main__":
    os.chdir(DIR)
    routine()
